home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 4.iso / public / cdsend / cdsend.c < prev    next >
C/C++ Source or Header  |  1994-08-01  |  9KB  |  359 lines

  1. /***********************************************************
  2. Copyright 1991, 1992 by Stichting Mathematisch Centrum, Amsterdam, The
  3. Netherlands.
  4.  
  5.                         All Rights Reserved
  6.  
  7. Permission to use, copy, modify, and distribute this software and its 
  8. documentation for any purpose and without fee is hereby granted, 
  9. provided that the above copyright notice appear in all copies and that
  10. both that copyright notice and this permission notice appear in 
  11. supporting documentation, and that the names of Stichting Mathematisch
  12. Centrum or CWI not be used in advertising or publicity pertaining to
  13. distribution of the software without specific, written prior permission.
  14.  
  15. STICHTING MATHEMATISCH CENTRUM DISCLAIMS ALL WARRANTIES WITH REGARD TO
  16. THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
  17. FITNESS, IN NO EVENT SHALL STICHTING MATHEMATISCH CENTRUM BE LIABLE
  18. FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  19. WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  20. ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
  21. OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  22.  
  23. ******************************************************************/
  24.  
  25. /* $Id: cdsend.c,v 1.2 1993/01/18 10:02:47 sjoerd Exp $ */
  26.  
  27. /*
  28.  * cdsend reads audio CD's over the SCSI bus, converts it to a format
  29.  * suitable for broadcast, and writes this converted data to standard
  30.  * output.  Optionally, the msuic is also played over the system's own
  31.  * speakers.
  32.  *
  33.  * Options.
  34.  *    -n    use non-degradable priority for the program (works
  35.  *        only if installed set-uid root).  The default is to
  36.  *        not change the priority.
  37.  *    -d    daemon mode.  When a CD finishes, hang around until
  38.  *        the next CD is put in the CD player and play it.  The
  39.  *        default is to exit after playing one CD.
  40.  *    -p    play the music over the system's speakers.  Default is
  41.  *        to not play the music.
  42.  *    -l    convert the data to 2-byte-per-sample linear format.
  43.  *        Default is to convert to 1-byte-per-sample U-law.
  44.  *    -c file    use the specified file to write the CD's
  45.  *        identification to.  Default is to write to the file
  46.  *        $HOME/.CDcatalognumber.
  47.  */
  48.  
  49. #include "cdsend.h"
  50. #include <unistd.h>
  51. #include <stdlib.h>
  52. #include <stdarg.h>
  53. #include <stdio.h>
  54. #include <signal.h>
  55. #include <limits.h>
  56. #include <sys/prctl.h>
  57. #include <sys/schedctl.h>
  58. #include <audio.h>
  59. #include <fcntl.h>
  60.  
  61. static ALport port;        /* audio output port */
  62. static int playaudio;        /* 1 iff we changed output rate */
  63. static volatile int silent = 1;    /* 1 iff we must be quiet */
  64. static volatile int stop_playing; /* 1 if we must stop playing */
  65. static int linear = 0;        /* 1 iff we produce linear (2-byte) output */
  66. static long oldparams[2] = {AL_OUTPUT_RATE, 0};
  67. static long newparams[2] = {AL_OUTPUT_RATE, AL_RATE_44100};
  68. static char *cdtoc, *cdid;
  69. static char cdcatnamebuf[1024];
  70. static char *cdcatname = cdcatnamebuf;
  71. extern char *db_get_TOC(CDPLAYER *, CDSTATUS *);
  72. extern char *db_get_id(CDPLAYER *, CDSTATUS *);
  73. extern char *optarg;
  74.  
  75. /* newer libraries use CDaddcallback and have a backward compatibility */
  76. /* define for CDsetcallback, we do it the other way round */
  77. #ifndef CDsetcallback
  78. #define CDaddcallback    CDsetcallback
  79. #endif
  80.  
  81. #define DEFCDCATNAME    ".CDcatalognumber"
  82.  
  83. /*
  84.  * Called when the program is interrupted.  This means that the audio
  85.  * params have to be reset, but only if we've changed them.
  86.  */
  87. static void
  88. die(int sig)
  89. {
  90.     if (playaudio)
  91.         ALsetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
  92.     exit(sig);
  93. }
  94.  
  95. /*
  96.  * Called when we get a SIGHUP.  This means that we must switch from
  97.  * playing the audio to being silent or vv.
  98.  */
  99. static void
  100. toggle(void)
  101. {
  102.     silent = !silent;
  103. }
  104.  
  105. /*
  106.  * Called when we get a SIGUSR1.  This means we have to stop reading
  107.  * the CD (and eject it).  This is useful if the CD player is needed
  108.  * for official business.
  109.  */
  110. static void
  111. eject(void)
  112. {
  113.     stop_playing = 1;
  114. }
  115.  
  116. /*
  117.  * Callback routine, called to do something to the audio data.
  118.  */
  119. static void
  120. handleaudio(void *arg, CDDATATYPES type, short *audio)
  121. {
  122.     if (!playaudio && !silent) {
  123.         /* we were silent but want to start playing, so set */
  124.         /* audio params */
  125.         ALgetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
  126.         ALsetparams(AL_DEFAULT_DEVICE, newparams, 2L);
  127.         playaudio = 1;
  128.     }
  129.     /* play the audio, but only if enough space in output port */
  130.     if (playaudio && ALgetfillable(port) >= CDDA_NUMSAMPLES)
  131.         ALwritesamps(port, audio, CDDA_NUMSAMPLES);
  132.     if (playaudio && silent) {
  133.         /* we were playing but want to be silent now, so reset */
  134.         /* audio params */
  135.         ALsetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
  136.         playaudio = 0;
  137.     }
  138.  
  139.     /* actually do the work we're being paid for */
  140.     convert_audio_and_print(audio, CDDA_NUMSAMPLES / 2, linear);
  141. }
  142.  
  143. /*
  144.  * Callback routine, called when the CD contains a catalog number.
  145.  * This routine writes the catalog number and the table of contents to
  146.  * a file.  Other programs can use this information to display the
  147.  * title of the CD being played.
  148.  */
  149. static void
  150. handlecatalog(void *arg, CDDATATYPES type, char *data)
  151. {
  152.     int i;
  153.     FILE *fp;
  154.  
  155.     for (i = 0; i < 13; i++)
  156.         if (data[i] != 0)
  157.             break;
  158.     if (i == 13)        /* catalog # is null, so ignore it */
  159.         return;
  160.  
  161.     if ((fp = fopen(cdcatname, "w")) == NULL)
  162.         return;
  163.     fprintf(fp, "hash=%s\n", cdid);
  164.     fprintf(fp, "toc=%s\n", cdtoc);
  165.     fprintf(fp, "catalog=");
  166.     for (i = 0; i < 13; i++)
  167.         fprintf(fp, "%c", *data++ + '0');
  168.     fprintf(fp, "\n");
  169.     fclose(fp);
  170. }
  171.  
  172. /*
  173.  * This routine is called to write identification information in the
  174.  * .CDcatalognumber file.  If there is a catalog number on the CD,
  175.  * this information is overwritten, but if there is no catalog number
  176.  * on the CD, the information written here can be used.
  177.  */
  178. static void
  179. nocatalog(CDSTATUS *cdstatus)
  180. {
  181.     FILE *fp;
  182.  
  183.     if ((fp = fopen(cdcatname, "w")) == NULL)
  184.         return;
  185.     fprintf(fp, "hash=%s\n", cdid);
  186.     fprintf(fp, "toc=%s\n", cdtoc);
  187.     fprintf(fp, "tmsf=%d.%d.%d.%d\n", cdstatus->last, cdstatus->total_min,
  188.         cdstatus->total_sec, cdstatus->total_frame);
  189.     fclose(fp);
  190. }
  191.  
  192. /*
  193.  * Wait for an audio CD to be inserted into the player.
  194.  */
  195. static void
  196. waitforcd(void)
  197. {
  198.     CDPLAYER *cdp;
  199.     CDSTATUS status;
  200.  
  201.     for (;;) {
  202.         for (;;) {
  203.             if ((cdp = CDopen(0, 0)) != 0)
  204.                 break;
  205.             /* opening the CD player failed; wait a while */
  206.             /* and try again */
  207.             sginap(60);
  208.         }
  209.  
  210.         /* opening the CD player succeeded; now wait until the */
  211.         /* player is ready */
  212.         for (;;) {
  213.             if (!CDgetstatus(cdp, &status)) {
  214.                 CDclose(cdp);
  215.                 break;
  216.             }
  217.             if (status.state == CD_READY) {
  218.                 if (!status.scsi_audio) {
  219.                     fprintf(stderr, "cdsend: CD-ROM player does not support audio CD's\n");
  220.                     CDclose(cdp);
  221.                     exit(1);
  222.                 }
  223.                 CDclose(cdp);
  224.                 return;
  225.             }
  226.             sginap(60);
  227.         }
  228.         sginap(60);
  229.     }
  230. }
  231.  
  232. /*
  233.  * This routine reads one whole CD and when it is finished, it ejects
  234.  * the CD.
  235.  */
  236. static void
  237. play(void)
  238. {
  239.     CDPLAYER *cdp;
  240.     CDPARSER *parser;
  241.     CDFRAME buf[12];
  242.     CDSTATUS cdstatus;
  243.     FILE *fd;
  244.     int i, n, first = 1;
  245.     ALconfig c;
  246.  
  247.     waitforcd();
  248.  
  249.     stop_playing = 0;
  250.  
  251.     cdp = CDopen(0, 0);
  252.     if (cdp == 0)
  253.         return;
  254.  
  255.     CDgetstatus(cdp, &cdstatus);
  256.     if (cdtoc)
  257.         free(cdtoc);
  258.     if (cdid)
  259.         free(cdid);
  260.     cdtoc = db_get_TOC(cdp, &cdstatus);
  261.     cdid = db_get_id(cdp, &cdstatus);
  262.  
  263.     nocatalog(&cdstatus);
  264.  
  265.     if ((parser = CDcreateparser()) == 0) {
  266.         perror("CDcreateparser");
  267.         exit(1);
  268.     }
  269.  
  270.     ALgetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
  271.     c = ALnewconfig();
  272.     ALsetwidth(c, AL_SAMPLE_16);
  273.     ALsetchannels(c, AL_STEREO);
  274.     port = ALopenport("cd", "w", c);
  275.  
  276.     CDaddcallback(parser, cd_audio, (CDCALLBACKFUNC) handleaudio, 0);
  277.     CDaddcallback(parser, cd_catalog, (CDCALLBACKFUNC) handlecatalog, 0);
  278.  
  279.     init_convert();
  280.  
  281.     while (!stop_playing) {
  282.         n = CDreadda(cdp, buf, 12);
  283.         if (first && n == 0) {
  284.             /* apparantly not an audio CD */
  285.             CDclose(cdp);
  286.             ALcloseport(port);
  287.             ALfreeconfig(c);
  288.             close(creat(cdcatname, 0666));
  289.             sginap(300);
  290.             return;
  291.         }
  292.         first = 0;
  293.         if (n < 0) {
  294.             perror("CDreadda");
  295.             exit(1);
  296.         }
  297.         if (n == 0)
  298.             break;
  299.  
  300.         for (i = 0; i < n && !stop_playing; i++)
  301.             CDparseframe(parser, &buf[i]);
  302.     }
  303.  
  304.     if (playaudio) {
  305.         ALsetparams(AL_DEFAULT_DEVICE, oldparams, 2L);
  306.         playaudio = 0;
  307.     }
  308.  
  309.     CDeject(cdp);
  310.     CDdeleteparser(parser);
  311.     CDclose(cdp);
  312.     ALcloseport(port);
  313.     ALfreeconfig(c);
  314.     close(creat(cdcatname, 0666));
  315. }
  316.  
  317. main(int argc, char **argv)
  318. {
  319.     int c;
  320.     int daemon = 0;
  321.  
  322.     sprintf(cdcatname, "%s/%s", getenv("HOME"), DEFCDCATNAME);
  323.  
  324.     while ((c = getopt(argc, argv, "c:npdl")) != EOF) {
  325.         switch (c) {
  326.         case 'c':
  327.             cdcatname = optarg;
  328.             break;
  329.         case 'n':
  330.             schedctl(NDPRI, 0, NDPNORMMAX);
  331.             break;
  332.         case 'd':
  333.             daemon = 1;
  334.             break;
  335.         case 'p':
  336.             silent = 0;
  337.             break;
  338.         case 'l':
  339.             linear = 1;
  340.             break;
  341.         }
  342.     }
  343.  
  344.     setuid(getuid());
  345.  
  346.     if (sigset(SIGINT, SIG_IGN) != SIG_IGN)
  347.         sigset(SIGINT, die);
  348.     sigset(SIGTERM, die);
  349.     sigset(SIGHUP, toggle);
  350.     sigset(SIGUSR1, eject);
  351.  
  352.     if (daemon)
  353.         for (;;)
  354.             play();
  355.     else
  356.         play();
  357.     die(0);
  358. }
  359.